package ar.edu.itba.pod.legajo45002;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.UnknownHostException;
import java.util.Collection;
import java.util.Collections;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Logger;
import org.joda.time.Duration;

import com.google.common.base.Preconditions;

import ar.edu.itba.node.NodeInformation;
import ar.edu.itba.pod.agent.market.Consumer;
import ar.edu.itba.pod.agent.market.Market;
import ar.edu.itba.pod.agent.market.Producer;
import ar.edu.itba.pod.agent.market.Resource;
import ar.edu.itba.pod.agent.runner.Simulation;
import ar.edu.itba.pod.time.TimeMapper;
import ar.edu.itba.pod.time.TimeMappers;

public class MySimulationApp {
	/*

	public static void main(String[] args) {
		boolean first = false;
		System.out.println(args[0]);
		System.out.println(args[1]);
		System.out.println(args[2]);
		//	System.out.println("i will connect to: "+ args[3]+" port: " + args[4]);

		NodeInformation info = new NodeInformation(args[0], Integer.valueOf(args[1]), args[2]);
		if (args[2].equals("1"))
			first = true;
		System.out.println(first);
		TimeMapper timeMapper = TimeMappers.oneSecondEach(Duration.standardHours(6));

		DistributedNodeImpl node = new DistributedNodeImpl(info, first,	timeMapper);
		node.connectToGroup(args[3], Integer.valueOf(args[4]), Integer.valueOf(args[5]) );
		System.out.println("Starting ...");
		System.out.println("agents in this node: " + DistributedNodeImpl.getSimulation().agentsRunning());
		try {
			node.getSimulation().startAndWait(Duration.standardMinutes(10));
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("Ending ...");

		try {
			Thread.sleep(100000);
			System.out.println("the coordinator is: " + DistributedNodeImpl.getCoordinator().id());
			Thread.sleep(10000);
			System.out.println(DistributedNodeImpl.allNodes().values());
			try {
				DistributedNodeImpl.getCluster().disconnectFromGroup(DistributedNodeImpl.getMynodeInfo());
			} catch (RemoteException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (NotBoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}
	 */




	private static Logger logger = Logger.getLogger(MySimulationApp.class);
	private static Options options = new Options();
	private static CommandLineParser parser = new GnuParser();
	private static Option connectedNodes;
	//      private static Option inbox;
	//      private static Option broadcastInbox;
	//private static Option addAgent;
	//      private static Option minAgentLoad;
	private static Option connect;
	private static Option createGroup;
	private static Option disconnectMe;
	private static Option help;
	private static Option coordinator;

	private static HelpFormatter formatter = new HelpFormatter();
	private static int port = 9999;
	private static Option setNodeId;
	private static Option getNodeId;
	private static Option addProducer;
	private static Option addConsumer;

	public static void main(String[] args) {
		//              PropertyConfigurator.configure("config/log4j.properties");

		//              try {
		//                      if (System.getSecurityManager() == null) {
		//                              System.setSecurityManager(new SecurityManager());
		//                      }
		//              } catch (SecurityException e) {
		//                      System.err.println("Error: could not set security manager: " + e);
		//              }
		BasicConfigurator.configure();
		MySimulationApp.initOptions();
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		CommandLine cmd;
		String addr = null;
		if (args.length == 1) {
			addr = args[0];
		} else if (args.length == 2) {
			addr = args[0];
			port = Integer.parseInt(args[1]);
		} else if ( args.length == 3) {
			addr = args[0];
			port = Integer.parseInt(args[1]);

		}else{

		}

		logger.info("node ip: " + addr + " port: " + port);
		NodeInformation info = new NodeInformation(args[0], Integer.valueOf(args[1]), args[2]);

		TimeMapper timeMapper = TimeMappers.oneSecondEach(Duration.standardHours(6));

		DistributedNodeImpl node = new DistributedNodeImpl(info, timeMapper);
		System.out.println("Starting ...");

		node.getSimulation().start(Duration.standardMinutes(10));
		Resource gold = new Resource("Mineral", "Gold");
		Resource copper = new Resource("Mineral", "Copper");
		Resource steel = new Resource("Alloy", "Steel");
	//	node.getSimulation().add(new Market("cooper market", copper));
	//	node.getSimulation().add(new Market("steel market", steel));

		while (true) {
			String line = null;

			try {
				line = br.readLine();
				String[] arg = line.split(" ");

				cmd = parser.parse(options, arg);

				if (cmd.hasOption(createGroup.getOpt())) {
					node.getSimulation().add(new Market("gold market", gold));

					DistributedNodeImpl.createGroup();
				} else if (cmd.hasOption(connect.getOpt())) {
					String[] values = cmd.getOptionValues(connect.getOpt());
					DistributedNodeImpl.connectToGroup(values[0], Integer.parseInt(values[1]));
				} else if (cmd.hasOption(help.getOpt())) {
					formatter.printHelp("options:", options);
				}  else if (cmd.hasOption(connectedNodes.getOpt())) {
					showConnectedNodes(DistributedNodeImpl.allNodes().values());
				} else if (cmd.hasOption(coordinator.getOpt())) {
					System.out.println(DistributedNodeImpl.getCoordinator());
				} else if (cmd.hasOption(getNodeId.getOpt())) {
					System.out.println(DistributedNodeImpl.getMynodeInfo().id());

				} else if (cmd.hasOption(addConsumer.getOpt())) {
					String[] values = cmd.getOptionValues(addConsumer.getOpt());
					Consumer c = getConsumer(values);
					if (c != null) {
						DistributedNodeImpl.addAgentToCluster(getConsumer(values));

						//DistributedNodeImpl.getSimulation().add(c);
						System.out.println(c + " added");
					}
				} else if (cmd.hasOption(addProducer.getOpt())) {
					String[] values = cmd.getOptionValues(addProducer.getOpt());
					Producer p = getProducer(values);
					if (p != null) {
						DistributedNodeImpl.addAgentToCluster(getProducer(values));
				//		DistributedNodeImpl.getSimulation().add(p);
						System.out.println(p + " added");
					}
				} else if (cmd.hasOption(disconnectMe.getOpt())) {
				//	String[] values = cmd.getOptionValues(disconnect.getOpt());
					DistributedNodeImpl.shutMeDown();
				
				}

			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (ParseException e) {
				System.out.println("wrong command");
			} catch (NumberFormatException e) {
				System.out.println("Invalid number");
			} 
		}
	}

	private static Producer getProducer(String[] values) {
		if ("steel".equals(values[0])) {
			return new Producer("steel mine",  new Resource("Alloy", "Steel"), Duration.standardDays(1), 5);
		} else if ("copper".equals(values[0])) {
			return new Producer("copper mine", new Resource("Mineral", "Copper"), Duration.standardDays(1), 10);
		} else if ("gold".equals(values[0])) {
			return new Producer("gold mine", new Resource("Mineral", "Gold"), Duration.standardDays(1), 1);
		}
		System.out.println("unknown consumer type");
		return null;
	}

	private static Consumer getConsumer(String[] values) {
		if ("steel".equals(values[0])) {
			return new Consumer("steel mine",  new Resource("Alloy", "Steel"), Duration.standardDays(1), 5);
		} else if ("copper".equals(values[0])) {
			return new Consumer("copper mine", new Resource("Mineral", "Copper"), Duration.standardDays(1), 10);
		} else if ("gold".equals(values[0])) {
			return new Consumer("gold mine", new Resource("Mineral", "Gold"), Duration.standardDays(1), 1);
		}
		System.out.println("unknown consumer type");
		return null;
	}

	private static void showConnectedNodes(Collection<NodeInformation> nodes) {
		Preconditions.checkNotNull(nodes);
		for (NodeInformation n: nodes) {
			System.out.println("-> " + n);
		}
	}

	private static void initOptions() {
		connectedNodes = new Option("connectedNodes", "show my connected nodes");
		options.addOption(connectedNodes);

		connect = new Option("connect", "connect to group with given node id (URI) as entry point");
		connect.setArgs(2);
		options.addOption(connect);

		createGroup = new Option("createGroup", "create a group for this node");
		options.addOption(createGroup);

		disconnectMe = new Option("disconnect", "disconnect from the group");
		//disconnectMe.setArgs(1);
		options.addOption(disconnectMe);

		help = new Option("help", "print options");
		options.addOption(help);

		coordinator = new Option("coordinator", "show the coordinator of this node");
		options.addOption(coordinator);

		getNodeId = new Option("getNodeId", "get node id");
		options.addOption(getNodeId);

		setNodeId = new Option("setNodeId", "set node id");
		setNodeId.setArgs(1);
		options.addOption(setNodeId);

		addProducer = new Option("addP", "add producer");
		addProducer.setArgs(1);
		options.addOption(addProducer);

		addConsumer = new Option("addC", "add consumer");
		addConsumer.setArgs(1);
		options.addOption(addConsumer);
	}

}
